package buyChance

import (
	//	"bufio"
	"fmt"
	//	"os"
	"sync"
	"unsafe"
)

import (
	"code.google.com/p/gcfg"
)
import (
	"r_log"
	"resource"
	"util"
	//	fl "utils/readFile"
)

var BCBase BuyChanceBase

type BuyChanceBase struct {
	Res         *resource.Resource
	DdsConf     DConf
	DdsSocket   *DataSocket
	BcLock      [util.PERIOD_COUNT]sync.Mutex
	BuyChances  [util.PERIOD_COUNT]*BuyChances
	ModeFilters [util.PERIOD_COUNT]map[util.StkID]*ModeFilter
	MfLocks     [util.PERIOD_COUNT]sync.Mutex
}

type BuyChances struct {
	Period     util.Period
	Date, Time util.DateAndTime
	BcMap      map[util.StkID]*BuyChance
	BcSlice    []*BuyChance
}

/*
type BuyChance struct {
	Stkid          util.StkID
	Price          [6]int32 //对于日线：前一K线收盘价、50%、70%、80%、85%、95%买成率价格；其它周期：除前一K线收盘价外其它为0
	Type           util.ModeType
	AvgTransaction int64
	Date, Time     util.DateAndTime
	Level          util.Level //滤网级别
	CountinueLose  bool       //连续多季度亏损
	//已分配的额度
	Lock                sync.Mutex //used to lock AssignedTransaction
	AssignedTransaction int64
}*/

type BuyChance struct {
	IsCY           bool //todo 是否创业板
	IsKC           bool //todo 是否科创版
	Stkid          util.StkID
	Price          [6]int32         //对于日线：前一K线收盘价、50%、70%、80%、85%、95%买成率价格；其它周期：除前一K线收盘价外其它为0
	Type           util.ModeType    //预警的模型类型
	AvgTransaction int64            //*100
	Date, Time     util.DateAndTime //预警的日期和时间
	Level          util.Level       //滤网级别
	NSuccRate      int32            // 成功率%*1000，如89%的成功率，为89000
	NProfitRate    int32            // 收益率%*1000，如5.5%的收益率，为5500
	NMaxLoss       int32            // 最大持仓亏损%*1000，如9%的亏损，为9000
	//SuccBuy        bool             //第一仓买入成功为true，加仓失败为false
	//FirstPrice     int32            //首仓价格，加仓失败时为0
	Market        util.MarketCode //所属市场
	CountinueLose bool            //连续多季度亏损
	//已分配的额度
	Lock                sync.Mutex //used to lock AssignedTransaction
	AssignedTransaction int64      //已分配的成交量（仅用于非对标账户）
	AssignedCount       int32      //已分配的次数（仅用于对标账户）
	IsInChooseMap       bool
}

type ModeFilter struct {
	Stkid      util.StkID
	Date, Time util.DateAndTime
	Level      [4]bool
}

type DConf struct {
	Port    int
	MaxSlot int
}

/*
type Schedule struct {
	Day, TwoHour, Hour, HalfHour ResetAndWait
	TenMin                       map[string]*CalcPeriod
	TimerSetting                 TimerConf
	ResetSchedule                ResetAndWait
}

type ResetAndWait struct {
	RestTime []util.DateAndTime //数据重置时间（结果数据，buychance数据），计算时需要等待buyChance的计算时间点
}

type CalcPeriod struct {
	StartTime, EndTime util.DateAndTime
	Duration           time.Duration
}

type TimerConf struct {
	TpsTimeout      time.Duration
	TpsNoonTimeout  time.Duration
	TpsNightTimeout time.Duration
	BuyChanceWait   time.Duration
}

var TimeStampList Schedule
var TimeList TimeSchedule

type TimeSchedule struct {
	RestTime [util.PERIOD_COUNT][]time.Time
	CalcTime []time.Time
}

func Init(confPath string) error {
	err := gcfg.ReadFileInto(&TimeStampList, confPath)
	if err != nil {
		return fmt.Errorf("gcfg.ReadFileInto(): %s", err.Error())
	}
	Update()
	return nil
}*/

var ModeTypeIdx ModeTypeIdxConv

type ModeTypeIdxConv struct {
	CHAODIE, ZHENDANG, NIUTOU, QUSHI IdxList
}

type IdxList struct {
	IDX []uint32
}

func InitModeType(confPath string) error {
	err := gcfg.ReadFileInto(&ModeTypeIdx, confPath)
	if err != nil {
		return fmt.Errorf("gcfg.ReadFileInto(): %s", err.Error())
	}
	InitModeMask()
	return nil
}

func InitModeMask() {
	for i := 0; i < len(ModeTypeIdx.CHAODIE.IDX); i++ {
		MODEMASK_CHAODIE[(ModeTypeIdx.CHAODIE.IDX[i]-1)/32] |= (1 << ((ModeTypeIdx.CHAODIE.IDX[i] - 1) % 32))
	}
	for i := 0; i < len(ModeTypeIdx.ZHENDANG.IDX); i++ {
		MODEMASK_ZHENGDANG[(ModeTypeIdx.ZHENDANG.IDX[i]-1)/32] |= (1 << ((ModeTypeIdx.ZHENDANG.IDX[i] - 1) % 32))
	}
	for i := 0; i < len(ModeTypeIdx.NIUTOU.IDX); i++ {
		MODEMASK_NIUTOU[(ModeTypeIdx.NIUTOU.IDX[i]-1)/32] |= (1 << ((ModeTypeIdx.NIUTOU.IDX[i] - 1) % 32))
	}
	for i := 0; i < len(ModeTypeIdx.QUSHI.IDX); i++ {
		MODEMASK_QUSHI[(ModeTypeIdx.QUSHI.IDX[i]-1)/32] |= (1 << ((ModeTypeIdx.QUSHI.IDX[i] - 1) % 32))
	}
}

var MODEMASK_CHAODIE [2]uint32
var MODEMASK_ZHENGDANG [2]uint32
var MODEMASK_NIUTOU [2]uint32
var MODEMASK_QUSHI [2]uint32

func GetModeType(NModeMask [2]uint32) util.ModeType {
	var mt util.ModeType = 0
	if (NModeMask[0]&MODEMASK_CHAODIE[0] > 0) || (NModeMask[1]&MODEMASK_CHAODIE[1] > 0) {
		mt |= util.MODE_CHAODIE
	}
	if (NModeMask[0]&MODEMASK_QUSHI[0] > 0) || (NModeMask[1]&MODEMASK_QUSHI[1] > 0) {
		mt |= util.MODE_QUSHI
	}
	if (NModeMask[0]&MODEMASK_ZHENGDANG[0] > 0) || (NModeMask[1]&MODEMASK_ZHENGDANG[1] > 0) {
		mt |= util.MODE_ZHENDANG
	}
	if (NModeMask[0]&MODEMASK_NIUTOU[0] > 0) || (NModeMask[1]&MODEMASK_NIUTOU[1] > 0) {
		mt |= util.MODE_NIUTOU
	}
	return mt
}

func Init(confPath string, res *resource.Resource) error {
	BCBase.DdsSocket = new(DataSocket)
	for p := util.PERIOD_DAY; p <= util.PERIOD_HHOUR; p++ {
		BCBase.ModeFilters[p] = make(map[util.StkID]*ModeFilter)
		BCBase.BuyChances[p] = new(BuyChances)
		BCBase.BuyChances[p].Period = p
		BCBase.BuyChances[p].BcMap = make(map[util.StkID]*BuyChance)
		BCBase.BuyChances[p].BcSlice = make([]*BuyChance, 0)
	}
	BCBase.Res = res
	err := gcfg.ReadFileInto(&BCBase, confPath)
	if err != nil {
		return fmt.Errorf("gcfg.ReadFileInto(): %s", err.Error())
	}
	go BCBase.DdsSocket.StartService(BCBase.DdsConf.Port, BCBase.DdsConf.MaxSlot)
	return nil
}

/*需要在调用函数中处理
bcb.BcLock[p].Lock()
bcb.MfLocks[p].Lock()
bcb.BcLock[p].Unlock()
bcb.MfLocks[p].UnLock()
*/
func (bcb *BuyChanceBase) AssignLevel(p util.Period) {
	for stkid, bc := range bcb.BuyChances[p].BcMap {
		//if bc.Type > util.MODE_QUSHI { //只有趋势需要过滤网
		//	bc.Level = util.LEVEL_A
		//	continue
		//}
		mf, ok := bcb.ModeFilters[p][stkid]
		if !ok { //如果股票没有滤网信息，则直接过
			bc.Level = util.LEVEL_A
		} else {
			bc.Level = mf.getFilterLevel()
		}
	}
}

func (mf *ModeFilter) getFilterLevel() util.Level {
	for l := util.LEVEL_A; l <= util.LEVEL_D; l++ {
		if mf.Level[l] {
			return l
		}
	}
	return util.LEVEL_UNKNOWN
}

//校验当前的buyChance是否有效
func (bcs *BuyChances) IsValid(calcPoint util.TimeStamp, isCalc bool) (isValid bool) {
	if len(bcs.BcSlice) == 0 {
		isValid = false
		return
	}
	//today := util.Today()
	//now := util.GetTime()
	nmd, _ := BCBase.Res.GetNextMarketDay(bcs.Date)
	var bcsValidTs util.TimeStamp

	switch bcs.Period {
	case util.PERIOD_DAY:
		bcsValidTs = util.GeneratorTimestamp(nmd, util.DateAndTime(145900))
	case util.PERIOD_2HOUR:
		switch bcs.Time {
		case util.DateAndTime(113000):
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, util.DateAndTime(145900))
		case util.DateAndTime(150000):
			bcsValidTs = util.GeneratorTimestamp(nmd, util.DateAndTime(112900))
		}
	case util.PERIOD_HOUR:
		switch bcs.Time {
		case util.DateAndTime(103000):
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, util.DateAndTime(112900))
		case util.DateAndTime(113000):
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, util.DateAndTime(135900))
		case util.DateAndTime(140000):
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, util.DateAndTime(145900))
		case util.DateAndTime(150000):
			bcsValidTs = util.GeneratorTimestamp(nmd, util.DateAndTime(102900))
		}
	case util.PERIOD_HHOUR:
		switch bcs.Time {
		case util.DateAndTime(113000):
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, util.DateAndTime(132900))
		case util.DateAndTime(150000):
			bcsValidTs = util.GeneratorTimestamp(nmd, util.DateAndTime(95900))
		default:
			bcsValidTs = util.GeneratorTimestamp(bcs.Date, bcs.Time+util.DateAndTime(2900))
		}
	}
	//isValid = bcsValidTs > util.GeneratorTimestamp(today, now)
	isValid = bcsValidTs > calcPoint
	if isCalc {
		r_log.Logger.Info("BuyChances: period=%v, date=%v, time=%v, isValid=%v", bcs.Period, bcs.Date, bcs.Time, isValid)
		if !isValid {
			bcs.BcMap = make(map[util.StkID]*BuyChance)
			bcs.BcSlice = make([]*BuyChance, 0)
		}
	}
	return
}

func (bcs *BuyChances) IsValidTest(calcPoint util.TimeStamp) (isValid bool) {
	/*
		bc := new(BuyChance)
		bc.Stkid = 54130
		bc.CountinueLose = false
		bc.Date = 20200225
		bc.Level = util.LEVEL_A
		bc.Type = util.MODE_CHAODIE
		bc.Price[0] = 5
		bc.Price[1] = 5
		bc.AvgTransaction = 5000000000
		bcs.BcSlice = append(bcs.BcSlice, bc)
		bcs.BcMap[bc.Stkid] = bc
		isValid = true*/

	//	ReadRawFlagDemo(0, "E:/demo/502/")

	util.Filters[0][0][0] = 0
	return
}

type Slice struct {
	Data unsafe.Pointer
	Len  int
	Cap  int
}

func Slice2Byte(s Slice, size int) []byte {
	s.Len *= size
	s.Cap *= size

	return *(*[]byte)(unsafe.Pointer(&s))
}

func Byte2Slice(b []byte, size int) *Slice {
	s := *(*Slice)(unsafe.Pointer(&b))
	s.Len /= size
	s.Cap /= size
	return &s
}

func Byte2Int32(src []byte) int32 {
	return (*(*[]int32)(unsafe.Pointer(Byte2Slice(src, 4))))[0]
}

func Byte2Int64(src []byte) int64 {
	return (*(*[]int64)(unsafe.Pointer(Byte2Slice(src, 8))))[0]
}

/*
func ReadRawFlagDemo(period util.Period, path string) {
	stk, stkid := ReadStkId()
	stkmap := make(map[string]uint32)
	for i := 0; i < len(stk); i++ {
		stkmap[stk[i]] = stkid[i]
	}
	var name string
	switch period {
	case util.PERIOD_DAY:
		name = "_502"
	case util.PERIOD_2HOUR:
		name = "_503"
	case util.PERIOD_HOUR:
		name = "_504"
	case util.PERIOD_HHOUR:
		name = "_505"
	}
	for i := 0; i < len(stk); i++ {
		//fmt.Println(i)
		fpath := path + stk[i] + name + ".dat"
		file, err := os.Open(fpath)
		if err != nil {
			//fmt.Println("Error:", err)
			//return ret
			continue
		}
		// 这个方法体执行完成后，关闭文件

		//index := 0
		//	fmt.Println(11111)
		for {
			b := make([]byte, 82)
			n := 0
			for n < 82 {

				m, err := file.Read(b)
				if err != nil {
					if m == 0 {
						goto nextmh
					} else {
						fmt.Println(err)
						return
					}
				}
				n += m
				//fmt.Println(index)
			}
			var rett SDds_I_Quantitative_RawFlag_V2

			rett.Decode(b)
			if rett.Info.DwDate == 20141127 {
				//fmt.Println(22222)
				buyChanceUpdateV2(rett, 0, false)
				//fmt.Println(33333)
			}
			//		fmt.Println(rett)
		}

	nextmh:
		file.Close()
	}
	return
}

func ReadStkId() ([]string, []uint32) {
	filePath := "D:/涨幅/MarketCodeList11.dat"
	f, err := os.Open(filePath)
	if err != nil {
		fmt.Println("股票列表错误")
		return nil, nil
	}
	defer f.Close()
	var ret1 []string
	var ret2 []uint32
	bfRd := bufio.NewReader(f)

	StockPool, _ := fl.GetAllFile("D:/涨幅/")
	//fmt.Println(StockPool)
	for i := 0; i < len(StockPool); i++ {
		b1 := make([]byte, 12)
		bfRd.Read(b1)
		if StockPool[i] != "SHHQ000001" {
			ret1 = append(ret1, string(b1[:10]))
		}
		b2 := make([]byte, 4)
		//b2 = b2[:4]
		bfRd.Read(b2)
		tslice := Byte2Slice(b2, 4)
		ttslice := *(*[]uint32)(unsafe.Pointer(tslice))
		if StockPool[i] != "SHHQ000001" {
			ret2 = append(ret2, ttslice[0])
		}
	}
	return ret1, ret2
}
*/
